# == Schema Information
# Schema version: 157
#
# Table name: workflows
#
#  id         :integer(11)     not null, primary key
#  name       :string(255)     
#  created_by :integer(11)     
#
class Workflow < ActiveRecord::Base
  has_many :actions, :class_name => "WorkflowAction", 
                     :order      => :position, 
                     :dependent  => :destroy

  validate :action_io_endpoints_match
  validates_associated :actions
  validates_inclusion_of :period, :in => %w{day week month year}, :allow_nil => true
  validates_inclusion_of :hour, :in => 0..23, :allow_nil => true
  validates_presence_of :owner_email, :if => Proc.new {|w| w.mail_report == true }
  
  def self.all_needing_execution(execution_time = Time.now)
    timed_workflows = Workflow.find(:all, :conditions => "period IS NOT NULL")
    timed_workflows.select do |workflow|
      execution_hour = workflow.hour ? "#{workflow.hour}:00" : ""
      frequency = (workflow.frequency || 1)
      period = workflow.period.to_sym
      
      date_diff = frequency.send(period).ago - Time.now
      next_scheduled_execution = Time.parse(execution_hour, execution_time + date_diff).utc
      workflow.last_successful_execution_at.nil? || next_scheduled_execution > workflow.last_successful_execution_at
    end
  end

  def reset
    self.actions.each(&:reset)
  end
  
  def save
    super
    self.actions.all?(&:save)
  end
  
  def execute
    return false unless save

    previous_output = nil 
    all_successful = self.actions.all? do |action|
      next unless action.in_list?
      action.input = previous_output unless action.first?
      success = action.execute
      previous_output = action.output if success
      success
    end
    
    # Set timestamps
    now = Time.now
    timestamps = {:last_attempted_execution_at => now}
    timestamps[:last_successful_execution_at] = now if all_successful
    self.update_attributes(timestamps)
    
    WorkflowMailer.deliver_workflow_report(self) if (mail_report && owner_email)
    
    return all_successful
  end
  
  def output
    self.actions.last.output
  end

  def output_class
    self.actions.last.output_class
  end
  
  def all_errors
    self.actions.collect { |a| a.errors.full_messages }.flatten
  end

  def report
    if ! defined?(Ruport::Reportable) or
       ! output_class.included_modules.include?(Ruport::Reportable) or
       output.empty?
       return nil
    end
    
    ids = output.compact.collect(&:id)
    output_class.report_table(:all, :conditions => {:id => ids})
  end

  protected
  
  def action_io_endpoints_match
    self.actions.each_with_index do |action, index|
      next_action = self.actions[index + 1]
      break if next_action.nil?
      
      unless next_action.accepts_input?(action)
        errors.add_to_base("#{next_action.name} cannot follow #{action.name}")
      end
    end
  end  
end
